Skip to content

一、基本概念

defer 能够让我们推迟执行某些函数调用,推迟到当前函数返回前才实际执行。defer 与 panic 和 recover 结合,形成了 Go 语言风格的异常与捕获机制。

使用场景:defer 语句经常被用于处理成对的操作,如文件句柄关闭、连接关闭、释放锁

优点:方便开发者使用

缺点:有性能损耗

二、实现原理

在 Go 语言中,defer 的先进后出(后进先出)的执行顺序是由编译器实现的,它通过在函数退出点插入 defer 语句的调用代码,确保这些语句按照与它们被添加到栈的相反顺序执行。

源代码:

func A(i int) {
    defer A1(i, 2\*i)
    if(i > 1) {
        defer A2("Hello", "eggo")
    }
    // 其它 code
    return
}

编译后(伪代码):

func A(i int) {
    // 其它 code
    if(i > 1){
        A2("Hello", "eggo")
    }
    A1(i, 2\*i)
}

三、代码示例

1、函数退出前,按照先进后出的顺序,执行 defer 函数


package main

import "fmt"

// defer:延迟函数执行,先进后出
func main() {
    defer fmt.Println("defer1")
    defer fmt.Println("defer2")
    defer fmt.Println("defer3")
    defer fmt.Println("defer4")
    fmt.Println("11111")
}

输出:

11111
defer4
defer3
defer2
defer1

2、panic 后的 defer 函数不会被执行(遇到 panic,如果没有捕获错误,函数会立刻终止)

package main

import "fmt"

// panic 后的 defer 函数不会被执行
func main() {
    defer fmt.Println("panic before")
    panic("发生 panic")
    defer func() {
    fmt.Println("panic after")
    }()
}

输出:

panic before
panic: 发生 panic

3、panic 没有被 recover 时,抛出的 panic 到当前 goroutine 最上层函数时,最上层程序直接异常终止

package main

import "fmt"

func F() {
    defer func() {
        fmt.Println("b")
    }()
    panic("a")
}

// 子函数抛出的 panic 没有 recover 时,上层函数时,程序直接异常终止
func main() {
    defer func() {
        fmt.Println("c")
    }()
    F()
    fmt.Println("继续执行")
}

​ 输出:

b
c
panic: a

4、panic 有被 recover 时,当前 goroutine 最上层函数正常执行

package main

import "fmt"

func F() {
    defer func() {
        if err := recover(); err != nil {
            fmt.Println("捕获异常:", err)
        }
        fmt.Println("b")
    }()
    panic("a")
}

func main() {
    defer func() {
        fmt.Println("c")
    }()
    F()
    fmt.Println("继续执行")
}

输出:

捕获异常: a
b
继续执行
c

木川工作室 (微信:mcmc2024)